﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;

namespace RMC
{
	internal class TcpServer : IDisposable
	{
		private TcpListener tcpListener;
		private List<TcpClient> clients = new List<TcpClient>();
		private int port;
		private object threadLock = new object();

		public TcpServer(int port)
		{
			this.port = port;
		}

		public void Start()
		{
			tcpListener = new TcpListener(IPAddress.Any, this.port);
			tcpListener.Start(2);

			tcpListener.BeginAcceptTcpClient(new AsyncCallback(tcpListener_AcceptTcpClient), null);
		}

		private void tcpListener_AcceptTcpClient(IAsyncResult asyn)
		{
			try
			{
				TcpClient tcpClient = tcpListener.EndAcceptTcpClient(asyn);

				lock (threadLock)
					clients.Add(tcpClient);

				ThreadPool.QueueUserWorkItem(new WaitCallback(tcpListener_ProcessTcpClient), tcpClient);

				tcpListener.BeginAcceptTcpClient(new AsyncCallback(tcpListener_AcceptTcpClient), null);
			}
			catch (ObjectDisposedException)
			{
				System.Diagnostics.Debugger.Log(0, "RMC", "\n OnClientConnection: Socket has been closed\n");
			}
			catch (SocketException)
			{
				System.Diagnostics.Debugger.Log(0, "RMC", "\n OnClientConnection: Socket has been closed\n");
			}
		}

		private void tcpListener_ProcessTcpClient(object tcpClient) 
		{
			TcpClient client = tcpClient as TcpClient;

			try
			{
				SendMessage("204 Connected.\r\n", client);
				using (StreamReader streamReader = new StreamReader(client.GetStream()))
				{
					while (client.Client.Connected)
					{
						string line = streamReader.ReadLine();
						
						if (line == null)
							break;

						line = line.Trim();

						if (line.Equals("exit", StringComparison.CurrentCultureIgnoreCase))
						{
							tcpListener_CloseTcpClient(client);
						}
						else
						{
							SendMessage(Command.Execute(line), client);
						}
						//SendMessage(">" + line.Trim() + "<\r\n", client);
					}
				}
			}
			catch (IOException) { }

			tcpListener_CloseTcpClient(client);
		}

		public void SendMessage(string message, TcpClient client)
		{
			if (client == null)
			{
				throw new ArgumentNullException("client");
			}

			byte[] buffer = System.Text.Encoding.UTF8.GetBytes(message);
			try
			{
				if (client.Client.Connected)
				{
					client.GetStream().Write(buffer, 0, buffer.Length);
				}
			}
			catch (IOException)
			{
				tcpListener_CloseTcpClient(client);
			}
		}

		private void tcpListener_CloseTcpClient(TcpClient client)
		{
			if (client != null)
			{
				lock (threadLock)
					clients.Remove(client);

				if (client.Connected)
					client.GetStream().Close();

				client.Close();
			}
		}

		public void Stop()
		{
			if (tcpListener != null)
				tcpListener.Stop();

			foreach (TcpClient client in clients)
			{
				if (client != null)
					tcpListener_CloseTcpClient(client);
			}
		}


		#region IDisposable Members

		public void Dispose()
		{
			Stop();
		}

		#endregion
	}
}
